package cn.tedu.mall.order.service.impl;

import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.pojo.domain.CsmallAuthenticationInfo;
import cn.tedu.mall.common.restful.JsonPage;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.order.mapper.OmsOrderItemMapper;
import cn.tedu.mall.order.mapper.OmsOrderMapper;
import cn.tedu.mall.order.service.IOmsCartService;
import cn.tedu.mall.order.service.IOmsOrderService;
import cn.tedu.mall.order.utils.IdGeneratorUtils;
import cn.tedu.mall.pojo.order.dto.OrderAddDTO;
import cn.tedu.mall.pojo.order.dto.OrderItemAddDTO;
import cn.tedu.mall.pojo.order.dto.OrderListTimeDTO;
import cn.tedu.mall.pojo.order.dto.OrderStateUpdateDTO;
import cn.tedu.mall.pojo.order.model.OmsCart;
import cn.tedu.mall.pojo.order.model.OmsOrder;
import cn.tedu.mall.pojo.order.model.OmsOrderItem;
import cn.tedu.mall.pojo.order.vo.OrderAddVO;
import cn.tedu.mall.pojo.order.vo.OrderDetailVO;
import cn.tedu.mall.pojo.order.vo.OrderListVO;
import cn.tedu.mall.product.service.order.IForOrderSkuService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@DubboService //将来秒杀功能会调用
@Service
@Slf4j
public class OmsOrderServiceImpl implements IOmsOrderService {
    //利用dubbo实现减少库存
    @DubboReference
    private IForOrderSkuService dubboSkuService;
    //删除购物车
    @Autowired
    private IOmsCartService cartService;
    //新增订单
    @Autowired
    private OmsOrderMapper orderMapper;
    //新增订单项
    @Autowired
    private OmsOrderItemMapper orderItemMapper;

    //因为当前项目使用Dubbo来操作其他微服务模块的数据，所以是一个分布式事务操作
    //利用Seata来保证事务的原子性
    @GlobalTransactional
    @Override
    public OrderAddVO addOrder(OrderAddDTO orderAddDTO) {
        //第一部分：收集信息，准备数据
        //将OrderAddDTO对象转换成OmsOrder
        OmsOrder order = new OmsOrder();
        BeanUtils.copyProperties(orderAddDTO,order);
        //收集信息和计算，单独编写成一个方法
        loadOrder(order);
        //上面完成的是订单OmsOrder的属性
        //下面完成订单项的赋值
        //OrderAddDTO对象中包含订单项的信息，获取这些订单项，判断是否为null
        List<OrderItemAddDTO> orderItemAddDTOList = orderAddDTO.getOrderItems();
        //判断订单项是否为null或者为空，如果为null终止业务执行
        if(orderItemAddDTOList==null || orderItemAddDTOList.isEmpty())
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"订单中必须包含订单项");
        //目前是List<OrderItemAddDTO>中保存了订单项，但是持久层新增OrderItem是List<OmsOrderItem>,所以要转换
        List<OmsOrderItem> omsOrderItems = new ArrayList<>();
        //遍历List<OrderItemAddDTO>进行赋值
        for (OrderItemAddDTO orderItemAddDTO : orderItemAddDTOList) {
            //先创建OmsOrderItem类型的对象
            OmsOrderItem orderItem = new OmsOrderItem();
            BeanUtils.copyProperties(orderItemAddDTO,orderItem);
            //orderItem和order一样，需要进行判断赋值
            loadOrderItem(orderItem);
            //将OmsOrder中的订单id赋值给OmsOrderItem
            orderItem.setOrderId(order.getId());
            //最后将OmsOrderItem对象保存到集合中
            omsOrderItems.add(orderItem);
            //到此位置我们第一部分的赋值过程完成
            //第二部分：完成操作数据库的过程
            //2.1 减少sku的库存
            //获取skuId
            Long skuId = orderItem.getSkuId();
            //利用dubbo远程调用product服务进行减少指定sku库存的操作
            int rows = dubboSkuService.reduceStockNum(skuId, orderItem.getQuantity());
            //判断执行的影响行数
            if(rows==0){
                log.warn("商品skuId:{},库存不足",skuId);
                //减少库存失败，大概率是库存不足导致的，所以要抛出异常
                throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"库存不足");
            }
            //2.2 删除购物车信息
            OmsCart omsCart = new OmsCart();
            omsCart.setUserId(order.getUserId());
            omsCart.setSkuId(skuId);
            cartService.removeUserCarts(omsCart);
        }
        //2.3 新增订单
        orderMapper.insertOrder(order);
        //2.4 新增订单项
        orderItemMapper.insertOrderItems(omsOrderItems);
        //最后收集生成订单过程中的一些重要数据，返回给前端
        //程序设计返回类型是OrderAddVO
        OrderAddVO orderAddVO = new OrderAddVO();
        orderAddVO.setId(order.getId()); //订单id
        orderAddVO.setSn(order.getSn()); //订单号
        orderAddVO.setCreateTime(order.getGmtOrder()); //订单生成时间
        orderAddVO.setPayAmount(order.getAmountOfActualPay()); //实际支付金额
        //千万别忘记返回
        return orderAddVO;
    }

    private void loadOrderItem(OmsOrderItem orderItem) {
        if (orderItem.getId()==null){
            Long id = IdGeneratorUtils.getDistributeId("order_item");
            orderItem.setId(id);
        }
        if(orderItem.getSkuId()==null){
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"订单商品中必须包含skuId");
        }
    }

    //新增订单业务中，需要收集和计算的order对象信息
    private void loadOrder(OmsOrder order) {
        //针对order对象必须具备但是为null的值进行赋值
        //1.判断id是否为null
        if(order.getId()==null){
            //使用Leaf生成分布式id
            Long id = IdGeneratorUtils.getDistributeId("order");
            order.setId(id);
        }
        //2.判断userId是否为null
        if(order.getUserId()==null){
            Long userId = getUserId();
            order.setUserId(userId);
        }
        //3.判断sn是否为null
        if(order.getSn()==null){
            String sn = UUID.randomUUID().toString();
            order.setSn(sn);
        }
        //4.判断订单状态是否为空
        if(order.getState()==null){
            order.setState(0); //默认是0，未支付状态
        }
        //5.为了保证生成订单时间和数据创建的时间以及最后修改时间一致
        //我们本次新增订单使用手动统一赋值
        if(order.getGmtOrder()==null){
            LocalDateTime now = LocalDateTime.now();
            order.setGmtOrder(now);
            order.setGmtCreate(now);
            order.setGmtModified(now);
        }
        //6.计算---用户支付的金额--数字类型
        //商品原价  运费  优惠金额  最后支付金额
        //判断商品原价是否为null，原价为null业务是无法正常执行的
        if (order.getAmountOfOriginalPrice()==null){
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"没有订单原价");
        }
        //判断运费是否null，运费为null需要转换成数字类型
        if(order.getAmountOfFreight()==null){
            order.setAmountOfFreight(new BigDecimal(0.0));
        }
        //判断优惠金额是否null，为null也需要转成数字类型
        if(order.getAmountOfDiscount()==null){
            order.setAmountOfDiscount(new BigDecimal(0.0));
        }
        //计算实际支付金额=原价+运费-优惠
        BigDecimal amountOfOriginalPrice = order.getAmountOfOriginalPrice();
        BigDecimal amountOfFreight = order.getAmountOfFreight();
        BigDecimal amountOfDiscount = order.getAmountOfDiscount();
        BigDecimal  actualPrice = amountOfOriginalPrice.add(amountOfFreight).subtract(amountOfDiscount);
        //给实际支付金额进行赋值
        order.setAmountOfActualPay(actualPrice);
    }
    //根据订单id修改订单状态
    @Override
    public void updateOrderState(OrderStateUpdateDTO orderStateUpdateDTO) {
        OmsOrder omsOrder = new OmsOrder();
        BeanUtils.copyProperties(orderStateUpdateDTO,omsOrder);
        orderMapper.updateOrderById(omsOrder);
    }
    //按指定时间分页查询订单列表
    @Override
    public JsonPage<OrderListVO> listOrdersBetweenTimes(OrderListTimeDTO orderListTimeDTO) {
        //因为默认查询最近 一个月的订单，所有参数对象中的起始时间和结束时间为空，需要赋默认值
        //即使不为空，我们也需要去判断起始时间和结束时间是否合理
        validaTimeAndLoadTimes(orderListTimeDTO);
        //获取用户id
        Long userId = getUserId();
        orderListTimeDTO.setUserId(userId);
        //设置分页条件
        PageHelper.startPage(orderListTimeDTO.getPage(),orderListTimeDTO.getPageSize());
        //执行查询
        List<OrderListVO> orderListVOS = orderMapper.selectOrderByTimes(orderListTimeDTO);
        return JsonPage.restPage(new PageInfo<>(orderListVOS));
    }

    private void validaTimeAndLoadTimes(OrderListTimeDTO orderListTimeDTO) {
        //取出时间
        LocalDateTime startTime = orderListTimeDTO.getStartTime();
        LocalDateTime endTime = orderListTimeDTO.getEndTime();
        //只要起始或结束时间有一个为null，就赋予默认值
        if(startTime==null || endTime==null){
            //起始时间是当前时间减去一个月
            startTime = LocalDateTime.now().minusMonths(1);
            //结束时间就是当前时间
            endTime = LocalDateTime.now();
            //把起始时间和结束时间赋值到orderListTimeDTO对象中
            orderListTimeDTO.setStartTime(startTime);
            orderListTimeDTO.setEndTime(endTime);
        }else{
            //检查startTime是否在endTime之前
            //要支持国际时间的判断，需要添加时区的修正
            if(endTime.toInstant(ZoneOffset.of("+8")).toEpochMilli()
                    <startTime.toInstant(ZoneOffset.of("+8")).toEpochMilli())
                //结束时间小于起始时间，不合理，需要抛出异常
                throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"结束时间小于起始时间");
        }
    }

    @Override
    public OrderDetailVO getOrderDetail(Long id) {
        return null;
    }
    //获取用户信息
    public CsmallAuthenticationInfo getUserInfo(){
        UsernamePasswordAuthenticationToken token =
        (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        if(token==null)
            throw new CoolSharkServiceException(ResponseCode.UNAUTHORIZED,"没有登录信息");
        CsmallAuthenticationInfo csmallAuthenticationInfo =
                (CsmallAuthenticationInfo) token.getCredentials();
        return csmallAuthenticationInfo;
    }
    //获取用户Id
    public Long getUserId(){
        return getUserInfo().getId();
    }
}
